package controllers

import (
	"errors"
	"fmt"
	"github.com/golang-jwt/jwt/v4"
	"gorm.io/gorm"
	"partyus_app/cos_api"
	"partyus_app/global"
	"partyus_app/models"
	"partyus_app/response"
	"partyus_app/utils"
	"regexp"
	"time"

	"github.com/asaskevich/govalidator"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis"
	"golang.org/x/crypto/bcrypt"
)

// Register 处理用户注册
func Register(ctx *gin.Context) {
	var req struct {
		Username   string `json:"username"`
		Email      string `json:"email"`
		Password   string `json:"password"`
		Repassword string `json:"repassword"`
		VerifyCode string `json:"verifyCode"` // 用户提交的验证码
	}

	// 解析请求体
	if err := ctx.Bind(&req); err != nil {
		response.CommonResp(ctx, 1, "参数错误", nil)
		return
	}

	// 检查必填字段是否为空
	if req.Username == "" || req.Email == "" || req.Password == "" || req.Repassword == "" || req.VerifyCode == "" {
		response.CommonResp(ctx, 1, "所有字段均为必填", nil)
		return
	}

	// 确认密码与重复密码是否一致
	if req.Password != req.Repassword {
		response.CommonResp(ctx, 1, "两次输入的密码不一致", nil)
		return
	}

	// 校验验证码(可选的读写分离)
	code, err := global.RedisRead().Get(req.Email).Result()
	if err == redis.Nil || code != req.VerifyCode {
		response.CommonResp(ctx, 1, "验证码错误或已过期", nil)
		return
	}

	// 检查邮箱是否已存在
	var user models.User
	if err := global.Db.Where("email = ?", req.Email).First(&user).Error; err == nil {
		response.CommonResp(ctx, 1, "用户已存在", nil)
		return
	}

	// 密码加密
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		response.CommonResp(ctx, 1, "无法生成密码", nil)
		return
	}

	// 创建用户
	newUser := models.User{
		Username: req.Username,
		Email:    req.Email,
		Password: string(hashedPassword),
	}
	if err := global.Db.Create(&newUser).Error; err != nil {
		response.CommonResp(ctx, 1, "注册失败", nil)
		return
	}

	// 删除 Redis 中的验证码
	global.RedisDB.Del(req.Email)

	response.CommonResp(ctx, 0, "注册成功", nil)
}

// Login 处理用户登录
func Login(ctx *gin.Context) {
	var user models.User
	input := models.User{}
	var res struct {
		Token string `json:"token"`
		ID    int    `json:"id"`
	}

	if err := ctx.Bind(&input); err != nil {
		response.CommonResp(ctx, 1, "参数错误", nil)
		return
	}

	// 查找用户
	if err := global.Db.Where("email = ?", input.Email).First(&user).Error; err != nil {
		response.CommonResp(ctx, 1, "用户不存在", nil)
		return
	}

	// 验证密码
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(input.Password)); err != nil {
		response.CommonResp(ctx, 1, "邮箱或密码错误", nil)
		return
	}

	// 生成 JWT 令牌
	token, err := utils.GenerateJWT(user.ID, user.Department)
	if err != nil {
		response.CommonResp(ctx, 1, "登录失败：生成令牌错误", nil)
		return
	}

	// 存储 token 和 userID 映射
	redisKeyToken := fmt.Sprintf("user:token:%s", token)                 // 用 token 作为键
	err = global.RedisDB.Set(redisKeyToken, user.ID, 24*time.Hour).Err() // 设置24小时过期
	if err != nil {
		response.CommonResp(ctx, 1, "登录失败：缓存失败", nil)
		return
	}

	// 填充响应数据
	res.Token = token
	res.ID = user.ID

	response.CommonResp(ctx, 0, "登录成功", res)
}

// RequestCode 发送验证码到邮箱
func RequestCode(ctx *gin.Context) {
	var req struct {
		Email string `json:"email"`
	}
	if err := ctx.Bind(&req); err != nil {
		response.CommonResp(ctx, 1, "参数错误", nil)
		return
	}

	// 发送验证码到用户邮箱
	_, err := utils.SendVerificationEmail(req.Email)
	if err != nil {
		response.CommonResp(ctx, 1, "发送验证码失败", nil)
		return
	}

	response.CommonResp(ctx, 0, "验证码已发送", nil)
}

func UploadAvatar(ctx *gin.Context) {
	var res struct {
		PicUrl string `json:"picUrl,omitempty"`
	}

	// 解析上传文件
	fileHeader, err := ctx.FormFile("file")
	if err != nil {
		response.CommonResp(ctx, 1, "文件上传失败", nil)
		return
	}

	filename := fileHeader.Filename
	isImage := utils.IsValidExtension(filename, utils.ImageExtensions)
	if !isImage {
		response.CommonResp(ctx, 1, "不支持的文件格式，仅支持图片", nil)
		return
	}

	// 生成新的文件名和 COS 对象键
	hashedFilename := utils.GenerateHashedFilename(filename)
	objectKey := fmt.Sprintf("avatars/%s", hashedFilename)

	// 打开文件流
	file, err := fileHeader.Open()
	if err != nil {
		response.CommonResp(ctx, 1, "文件读取失败", nil)
		return
	}
	defer file.Close()

	// 画质压缩图片，不改变尺寸，压缩质量75（可调）
	compressedReader, err := utils.CompressImageToJPEG(file, 75)
	if err != nil {
		// 兜底
		compressedReader = file
	}

	// 上传至 COS
	fileURL, err := cos_api.UploadReaderToCOS(compressedReader, objectKey)
	if err != nil {
		response.CommonResp(ctx, 1, "上传至 COS 失败", nil)
		return
	}

	res.PicUrl = fileURL

	// 获取当前用户 ID
	userIDVal, ok := ctx.Get("userID")
	if !ok {
		response.CommonResp(ctx, 1, "请重新登录", nil)
		return
	}
	userID, ok := userIDVal.(int)
	if !ok {
		response.CommonResp(ctx, 1, "用户 ID 类型错误", nil)
		return
	}

	// 更新数据库
	err = global.Db.Model(&models.User{}).Where("id = ?", userID).Update("avatar", res.PicUrl).Error
	if err != nil {
		response.CommonResp(ctx, 1, "更新用户头像失败", nil)
		return
	}

	// 成功响应
	response.CommonResp(ctx, 0, "操作成功！", res)
}

func RequestCodeByID(ctx *gin.Context) {
	// 从上下文获取 userID
	userID, exists := ctx.Get("userID")
	if !exists {
		response.CommonResp(ctx, 1, "用户未登录或未授权", nil)
		return
	}

	// 将 userID 转为 int 类型
	userIDInt, ok := userID.(int)
	if !ok {
		response.CommonResp(ctx, 1, "用户 ID 无效", nil)
		return
	}

	// 查询用户信息以获取 Email
	var user models.User
	if err := global.Db.First(&user, userIDInt).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.CommonResp(ctx, 1, "用户不存在", nil)
		} else {
			response.CommonResp(ctx, 1, "查询用户失败", nil)
		}
		return
	}

	// 发送验证码到用户邮箱
	_, err := utils.SendVerificationEmail(user.Email)
	if err != nil {
		response.CommonResp(ctx, 1, "发送验证码失败", nil)
		return
	}

	response.CommonResp(ctx, 0, "验证码已发送", nil)
}

func UpdatePassword(ctx *gin.Context) {
	var req struct {
		Password   string `json:"password" binding:"required"`
		Email      string `json:"email"`
		VerifyCode string `json:"verifyCode"` // 用户提交的验证码
	}

	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.CommonResp(ctx, 1, "参数错误", nil)
		return
	}

	// 校验验证码
	code, err := global.RedisDB.Get(req.Email).Result()
	if err == redis.Nil || code != req.VerifyCode {
		response.CommonResp(ctx, 1, "验证码错误或已过期", nil)
		return
	}

	// 获取当前用户的角色和 ID
	userID, exists1 := ctx.Get("userID")

	if !exists1 {
		response.CommonResp(ctx, 1, "用户未登录或未授权", nil)
		return
	}

	targetUserID := userID.(int)

	var user models.User
	if err := global.Db.Where("id = ? AND deleted_at IS NULL", targetUserID).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.CommonResp(ctx, 1, "用户不存在或已删除", nil)
		} else {
			response.CommonResp(ctx, 1, "查询用户失败", nil)
		}
		return
	}

	// 加密新密码
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		response.CommonResp(ctx, 1, "密码加密失败", nil)
		return
	}

	// 更新密码
	if err := global.Db.Model(&user).Update("password", string(hashedPassword)).Error; err != nil {
		response.CommonResp(ctx, 1, "密码更新失败", nil)
		return
	}

	response.CommonResp(ctx, 0, "用户密码重置成功", nil)
}

func VerifyInfo(ctx *gin.Context) {
	// 定义请求结构体
	var req struct {
		Email string `json:"email"`
		Phone string `json:"phone"`
	}

	// 解析 JSON 请求体
	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.CommonResp(ctx, 1, "参数错误", nil)
		return
	}

	// 验证结果结构
	type ErrorInfo struct {
		EmailValid bool `json:"emailValid"`
		PhoneValid bool `json:"phoneValid"`
	}

	errorInfo := ErrorInfo{
		EmailValid: false,
		PhoneValid: false,
	}

	// 验证邮箱格式，使用 govalidator
	emailValid := govalidator.IsEmail(req.Email)
	errorInfo.EmailValid = emailValid

	// 验证手机号格式，使用自定义的正则表达式
	phoneRegex := `^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$`
	phoneValid, _ := regexp.MatchString(phoneRegex, req.Phone)
	errorInfo.PhoneValid = phoneValid

	// 如果两者都合法，返回成功
	if errorInfo.EmailValid && errorInfo.PhoneValid {
		response.CommonResp(ctx, 0, "验证成功", nil)
		return
	}

	// 如果有一项或多项不合法，返回错误信息
	response.CommonResp(ctx, 1, "格式错误", errorInfo)
}

// 超级管理员重置用户密码
func ResetUserPassword(ctx *gin.Context) {
	var req struct {
		ID int `json:"id" binding:"required"`
	}

	if err := ctx.ShouldBindJSON(&req); err != nil {
		response.CommonResp(ctx, 1, "参数错误", nil)
		return
	}

	userID, exists1 := ctx.Get("userID")
	role, exists2 := ctx.Get("role")

	if !exists1 || !exists2 {
		response.CommonResp(ctx, 1, "用户未登录或未授权", nil)
		return
	}

	_, ok1 := userID.(int)
	currentRole, ok2 := role.(int)

	if !ok1 || !ok2 {
		response.CommonResp(ctx, 1, "用户信息解析失败", nil)
		return
	}

	if currentRole != 0 {
		response.CommonResp(ctx, 1, "权限不足，非根管理员", nil)
		return
	}

	var user models.User
	if err := global.Db.Where("id = ? AND deleted_at IS NULL", req.ID).First(&user).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			response.CommonResp(ctx, 1, "目标用户不存在或已删除", nil)
		} else {
			response.CommonResp(ctx, 1, "查询目标用户失败", nil)
		}
		return
	}

	// 默认重置密码为 123456
	newPassword := "123456"

	// 加密
	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
	if err != nil {
		response.CommonResp(ctx, 1, "密码加密失败", nil)
		return
	}

	if err := global.Db.Model(&user).Update("password", string(hashedPassword)).Error; err != nil {
		response.CommonResp(ctx, 1, "密码更新失败", nil)
		return
	}

	response.CommonResp(ctx, 0, "用户密码重置成功", nil)
}

func ValidateToken(c *gin.Context) {
	tokenString := c.GetHeader("Authorization")
	if tokenString == "" {
		response.CommonResp(c, -1, "token未提供", nil)
		return
	}

	// 解析和验证令牌
	token, err := utils.ParseString2Token(tokenString)

	if err != nil || !token.Valid {
		response.CommonResp(c, -1, "token过期，请重新登录", nil)
		return
	}
	//检测过期token
	redisKey := fmt.Sprintf("user:token:%s", tokenString)
	_, isExist := global.RedisRead().Get(redisKey).Result()
	if isExist == redis.Nil {
		response.CommonResp(c, -1, "token 已失效或未登录", nil)
		return
	} else if isExist != nil {
		response.CommonResp(c, -1, "Redis 检查失败", nil)
		return
	}
	//log.Println("debug:token存在redis中,", tokenString)
	// 提取 Claims
	if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		response.CommonResp(c, 0, "", nil)
		return
	} else {
		response.CommonResp(c, -1, "token无效或解析失败", nil)
		return
	}
}
